/*
Hermes Core: General Purpose Mobile Agent Platform
Copyright (C) 2006 UNICAM - Università degli Studi di Camerino

Group leaders: Flavio Corradini and Emanuela Merelli

Developers: Ezio Bartocci, Diego Bonura, Rosario Culmone,
Davide Lorenzoli, Leonardo Mariani, Francesca Piersigilli, 
Lorenzo Scortichini, Leonardo Vito.

GNU Lesser Public License

This library is free software; you can redistribute it 
and/or modify it under the terms of the GNU Lesser General 
Public License as published by the Free Software Foundation, 
version 2.1 of the License.

This library is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU Library General Public License for more details.

You should have received a copy of the GNU Library General Public 
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.

Emanuela Merelli
Dipartimento di Matematica ed Informatica
Università di Camerino
via Madonna delle Carceri, 62032 Camerino, Italy
E.Mail: emanuela.merelli@unicam.it
*/
package hermesV2.basicServices.mobility;

import hermesV2.AgentThread;
import hermesV2.CurrentAgentLocation;
import hermesV2.Envelope;
import hermesV2.PlaceAddress;

import hermesV2.agent.UserAgent;

import hermesV2.core.id.Identify;
import hermesV2.core.id.Trace;

import hermesV2.core.net.EnvelopeIO;

import hermesV2.util.HermesLogger;

import java.io.IOException;

import android.util.Log;


/**
 * Offers the mobility service to the agent layer. Implements the Move
 * interface  used by the agent layer.
 *
 * @author Diego Bonura
 */
public class MobilityManager implements Move {
    /**
     * The MessageIO interface of the SendReceiver component of the core layer.
     */
    private EnvelopeIO envelopeIO;
    /** The Identify interface of the Id component of the core layer. */
    private Identify identify;
    /** The Trace interface of the Id component of the core layer. */
    private Trace trace;
    /** The curren PlaceAddress. */
    private PlaceAddress currentPlaceAddress;

    /**
     * Creates a new Mobility object.
     *
     * @param currentPlaceAddress the current PlaceAddress.
     */
    public MobilityManager(PlaceAddress currentPlaceAddress) {
        this.currentPlaceAddress = currentPlaceAddress;
    }

    /**
     * Offers  to the agent layer a way to migrate agents.
     *
     * @param receiverPlaceAddress the receiver PlaceAddress.
     * @param agent the agent involved in the migration.
     *
     * @throws TransportFailedException the migrations failed.
     * @throws ThreadDeath Used to stop the execution of the method.
     */
    public void go(PlaceAddress receiverPlaceAddress, UserAgent agent) throws TransportFailedException, ThreadDeath {
            AgentThread at = identify.getAgentThread(agent.getIdentificator());
        	if (receiverPlaceAddress.equals(currentPlaceAddress)){
        	at.restartAgent();	
        	}else{
        	try {
                //It erases the presence of the current agent in the current place
        	    identify.eraseAgentIdentificator(agent.getIdentificator());
        	
            Envelope<?> reply = envelopeIO.sendEnvelope(new Envelope<UserAgent>(Envelope.AGENT_ENTER, agent, currentPlaceAddress, receiverPlaceAddress));
            String message = (String) reply.getObject();

            if (reply.getType() == Envelope.RECEPTION_OK) {

                
/*
                //It upgrades the position of the agent in the borns table
                //First it checks if the update has to be locally
                //then it tries to update the location in remote
                if (trace.updateAgentAddress(agent.getIdentificator(), receiverPlaceAddress) == null) {
                    reply = envelopeIO.sendEnvelope(new Envelope(Envelope.UPDATED_ADDRESS, new CurrentAgentLocation(agent.getIdentificator(), receiverPlaceAddress), currentPlaceAddress, agent.getIdentificator().getBornPlaceAddress()));

                    if (reply.getType() == Envelope.RECEPTION_OK) {
                        HermesLogger.log(HermesLogger.INFO, "Update address done Agent:" + agent.getIdentificator());
                    } else {
                        HermesLogger.log(HermesLogger.INFO, "Error on update address Agent:" + agent.getIdentificator());
                    }
                }
*/
                if (at != null) {
                    try {
                    	at.interrupt();
                        //at.stop();
                    } catch (ThreadDeath td) {
                    	Log.e("Errori",td.getMessage()+" Non bene!!");
                        throw td;
                    }
                }
            } else {
            	identify.addAgentIdentificator(agent.getIdentificator(),at);
            	//It upgrades the position of the agent in the borns table with the old current position
            	if (trace.updateAgentAddress(agent.getIdentificator(), currentPlaceAddress) == null) {
                    reply = envelopeIO.sendEnvelope(new Envelope<CurrentAgentLocation>(Envelope.UPDATED_ADDRESS, new CurrentAgentLocation(agent.getIdentificator(), currentPlaceAddress), currentPlaceAddress, agent.getIdentificator().getBornPlaceAddress()));
                    if (reply.getType() == Envelope.RECEPTION_OK) {
                        HermesLogger.log(HermesLogger.DEBUG, "Update address done Agent:" + agent.getIdentificator());
                    } else {
                        HermesLogger.log(HermesLogger.WARNING, "Error on update address Agent:" + agent.getIdentificator());
                    }
                }
            	
            	
                if (message == null) {
                	
                    throw new TransportFailedException("Unable to connect with the remote place");
                } else {
                    throw new TransportFailedException(message);
                }
            }
        } catch (ClassNotFoundException cnfe) {
        	identify.addAgentIdentificator(agent.getIdentificator(),at);
            throw new TransportFailedException("Message contains a class unknown");
        } catch (IOException ioe) {
        	identify.addAgentIdentificator(agent.getIdentificator(),at);
            throw new TransportFailedException(ioe.getMessage());
        }
    }}

    /**
     * Sets the EnvelopeIO interface of the SendReceiver component of the core
     * layer.
     *
     * @param envelopeIO the EnvelopeIO interface of the SendReceiver component
     *        of the core layer.
     */
    public void setEnvelopeIO(EnvelopeIO envelopeIO) {
        this.envelopeIO = envelopeIO;
    }

    /**
     * Sets the Identify interface of the ID component of the core layer.
     *
     * @param identify the Identify interface of the ID component of the core
     *        layer.
     */
    public void setIdentify(Identify identify) {
        this.identify = identify;
    }

    /**
     * Sets the Trace interface of the ID component of the core layer.
     *
     * @param trace the Trace interface of the ID component of the core layer.
     */
    public void setTrace(Trace trace) {
        this.trace = trace;
    }
}
